package sicnu.cs.ich.ich.ranking.schedule;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import sicnu.cs.ich.api.common.Constants;
import sicnu.cs.ich.api.common.enums.ConfigEnum;
import sicnu.cs.ich.api.domain.dto.IchHotDTO;
import sicnu.cs.ich.api.domain.entity.IchRankRecord;
import sicnu.cs.ich.common.services.IConfigService;
import sicnu.cs.ich.common.services.impl.RedisService;
import sicnu.cs.ich.ich.ranking.mapper.IchRankRecordMapper;
import sicnu.cs.ich.ich.ranking.service.IIchRankingService;

import java.util.*;

/**
 * 开启关于Ich热榜每个小时自动刷新的请求
 *
 * @author CaiKe
 * @date 2021/11/14 20:32
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class IchHitRankingSchedule {
    private final IIchRankingService rankingService;
    private final IConfigService configService;
    private final RedisService redisService;
    private final IchRankRecordMapper ichRankRecordMapper;

//    /**
//     * 每天 6点到22点，每2小时触发一次
//     */
//    @Scheduled(cron = "0 1 6/2 * * ? ")
//    public void sortIchHitRanking() throws RemotingException, MQClientException, MQBrokerException {
//        log.info("定时任务触发，执行非遗热力榜排序。");
//        try {
//            rankingService.sort();
//            final List<Integer> ids = rankingService.getIchRankingIds();
//            log.info("已通知ICH服务缓存热点数据：{}", ids.toString());
//        } catch (InterruptedException e) {
//            log.error("执行非遗排序时发生错误", e);
//        }
//    }

    /**
     * 每天6点到22点，每两小时触发一次配置信息备份
     */
    @Scheduled(cron = "0 0 6/2 * * ? ")
    public void storeIchConfig() {
        log.info("定时任务触发，执行非遗热力榜数据配置自动备份");
        final HashMap<ConfigEnum, String> config = rankingService.exportHitConfig();
        try {
            configService.storeConfigs(config);
        } catch (Exception exception) {
            log.error("定时备份非遗热力帮数据出现错误。" + exception);
        }
    }

    /**
     * 每日晚11点，更新排行榜，将数据同步到数据库中， redis中只保留前10
     */
    @Scheduled(cron = "0 0 23 * * ? ")
    public void storeIchProvinceRank() throws JsonProcessingException {
        log.info("定时任务触发，执行非遗省级排行榜数据");
        long start = System.currentTimeMillis();
        var keys = redisService.keys(Constants.REDIS_RANK_ICH_PROVINCE + "*");
        var list = new ArrayList<IchRankRecord>(keys.size());
        // 循环获取redis中所有的省级非遗排行榜
        for (String key : keys) {
            // 某个省下的非遗信息
            var entry = redisService.getZset(key, 0, -1);
            // 将某个省下的非遗排行榜转为json入库
            var json = new ObjectMapper().writeValueAsString(entry);
            list.add(new IchRankRecord(key, json));
            // 获取当前省下的非遗热度，并进行入库
            var ichRankRecordList = new ArrayList<IchHotDTO>();
            for (ZSetOperations.TypedTuple<Object> typedTuple : entry) {
                var record = (IchHotDTO) typedTuple.getValue();
                if (record != null) {
                    int updateHot = Objects.requireNonNull(typedTuple.getScore()).intValue();
                    if (updateHot>0){
                        record.setHot(updateHot);
                        ichRankRecordList.add(record);
                    }
                }
            }
            // 批量更新非遗的访问量热度
            rankingService.batchIncrementIchHot(ichRankRecordList);
            // 清除当日排行榜
            redisService.removeZset(key, 0, -1);
            // 数据库中获取新的20个非遗数据，并放入redis中

            var ichHotDTOList = rankingService.getProvinceIchHotDTOs(key.substring(Constants.REDIS_RANK_ICH_PROVINCE.length()),20);
            var newRank = new HashSet<ZSetOperations.TypedTuple<Object>>();
            for (IchHotDTO hotDTO : ichHotDTOList) {
                hotDTO.setHot(0);
                newRank.add(new DefaultTypedTuple<>(hotDTO, (double) 0));
            }
            redisService.cacheZset(key, newRank);
            //var rank = redisService.getZset(key, 0, 20);
            //// 清除当日排行榜
            //redisService.removeZset(key, 0, -1);
            //// 重置排行榜
            //int i = 20;
            //var newRank = new HashSet<ZSetOperations.TypedTuple<Object>>();
            //for (ZSetOperations.TypedTuple<Object> tuple : rank) {
            //    int score = (int) (tuple.getScore() != null ? tuple.getScore() : 0);
            //    if (score >> 1 < i) {
            //        score = i--;
            //    }
            //    newRank.add(new DefaultTypedTuple<>(tuple.getValue(), (double) score));
            //}
            //if (!newRank.isEmpty()) {
            //    redisService.cacheZset(key, newRank);
            //}
        }
        if (!list.isEmpty()) {
            // 当日排行榜入库
            ichRankRecordMapper.storeOneDayRank(list);
        }
        log.info("排行榜更新结束,耗时：" + (System.currentTimeMillis() - start) + "ms");
    }

    /**
     * 删除Redis中非遗点赞信息的统计
     * 每天23:59:00启动
     */
    @Scheduled(cron = "0 59 23 * * ? ")
    public void clearRedisIchLiking() {
        rankingService.clearLikeNum();
        log.info("定时任务，删除Redis中非遗点赞信息的统计");
    }
}